package com.atguigu.springsecurity.service.impl;

import com.atguigu.springsecurity.entity.User;
import com.atguigu.springsecurity.mapper.UserMapper;
import com.atguigu.springsecurity.service.UserService;
import com.atguigu.springsecurity.util.JwtUtil;
import com.atguigu.springsecurity.util.RedisCache;
import com.atguigu.springsecurity.util.ResponseResult;
import com.atguigu.springsecurity.vo.LoginUserVo;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisCache redisCache;

    /**
     * 登录方法
     * @param user
     * @return
     */
    @Override
    public ResponseResult login(User user) {

        // AuthenticationManager authenticate 进行用户的认证
        // 将用户输入的用户名和面封装成了authentication
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
        // 让provideManager来帮助我们进行认证
        // 如果认证没有通过， 给出对应的提示
        // 注意我们这一步会去调用userDetial的接口实现类
        // (将UserDetialServiceImpl里面查询到的user放置在里面 框架内部就会帮助我们将loginUserVo设置进去)
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        if(authenticate == null) {
            // 认证为null
            throw new RuntimeException("登录失败");
        }

        // 如果认证通过了， 使用userId生成jwt  jwt存入ResponseResult并且返回
        LoginUserVo principal = (LoginUserVo) authenticate.getPrincipal();
        // 得到用户id
        Long id = principal.getUser().getId();
        // 根据userId生成jwt
        String jwt = JwtUtil.createJWT(id.toString());

        Map<String, String> map = new HashMap<>();
        map.put("token", jwt);

        // 把用户完整信息存入redis， userId作为key
        redisCache.setCacheObject("login:" + id.toString(), principal);
        return new ResponseResult(200, "登录成功", map);
    }

    /**
     * 登出功能
     * @return
     */
    @Override
    public ResponseResult logout() {
        // 获取SecurityHolder中的信息id
        // 不需要再去删除了 因为我们获取不到redis里面的消息就直接抛出异常了

        // 现在是新的线程我们能不能获取到这个里面的值喃，是不是新的喃?
        // 答案是会的，因为我们的Jwt里面会先拦截然后给SecurityContextHolder设置值
        UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUserVo principal = (LoginUserVo) authentication.getPrincipal();
        Long userId = principal.getUser().getId();
        String redisKey = "login:" + userId.toString();
        // 删除redis中userId这个
        redisCache.deleteObject(redisKey);
        return new ResponseResult(200, "退出登录成功");
    }
}
